package com.engine.salary.service.impl;

import com.alibaba.fastjson.JSON;
import com.api.formmode.mybatis.util.SqlProxyHandle;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.hrmelog.entity.dto.LoggerContext;
import com.engine.salary.config.SalaryElogConfig;
import com.engine.salary.encrypt.EncryptUtil;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.hrm.dto.HrmInfoDTO;
import com.engine.salary.entity.hrm.param.HrmQueryParam;
import com.engine.salary.entity.siaccount.param.InsuranceAccountDetailParam;
import com.engine.salary.entity.siaccount.param.RecessionParam;
import com.engine.salary.entity.siaccount.po.InsuranceAccountBatchPO;
import com.engine.salary.entity.siaccount.po.InsuranceAccountDetailPO;
import com.engine.salary.entity.siarchives.po.InsuranceArchivesBaseInfoPO;
import com.engine.salary.entity.taxagent.po.TaxAgentPO;
import com.engine.salary.enums.OperateTypeEnum;
import com.engine.salary.enums.auth.AuthFilterTypeEnum;
import com.engine.salary.enums.siaccount.BillStatusEnum;
import com.engine.salary.enums.siaccount.PaymentStatusEnum;
import com.engine.salary.enums.siaccount.ProjectTypeEnum;
import com.engine.salary.enums.sicategory.DeleteTypeEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.datacollection.EmployMapper;
import com.engine.salary.mapper.siaccount.InsuranceAccountBatchMapper;
import com.engine.salary.mapper.siaccount.InsuranceAccountDetailMapper;
import com.engine.salary.mapper.siarchives.InsuranceBaseInfoMapper;
import com.engine.salary.mapper.taxagent.TaxAgentMapper;
import com.engine.salary.service.SIAccountService;
import com.engine.salary.service.SIRecessionService;
import com.engine.salary.service.SalaryEmployeeService;
import com.engine.salary.service.TaxAgentService;
import com.engine.salary.service.auth.AuthService;
import com.engine.salary.service.auth.AuthServiceImpl;
import com.engine.salary.util.SalaryAssert;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import weaver.hrm.User;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: sy
 * @Description: 社保福利台账-退差实现类
 * @Date: 2022/11/15
 **/
public class SIRecessionServiceImpl extends Service implements SIRecessionService {

    private EncryptUtil encryptUtil = new EncryptUtil();

    private InsuranceAccountDetailMapper getInsuranceAccountDetailMapper() {
        return SqlProxyHandle.getProxy(InsuranceAccountDetailMapper.class);
    }

    private TaxAgentService getTaxAgentService(User user) {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }

    private EmployMapper getEmployMapper() {
        return MapperProxyFactory.getProxy(EmployMapper.class);
    }

    private SIAccountService getSIAccountService(User user) {
        return ServiceUtil.getService(SIAccountServiceImpl.class, user);
    }

    private InsuranceAccountBatchMapper getInsuranceAccountBatchMapper() {
        return MapperProxyFactory.getProxy(InsuranceAccountBatchMapper.class);
    }

    private TaxAgentMapper getTaxAgentMapper() {
        return MapperProxyFactory.getProxy(TaxAgentMapper.class);
    }

    private SalaryEmployeeService getSalaryEmployeeService(User user) {
        return ServiceUtil.getService(SalaryEmployeeServiceImpl.class, user);
    }

    private InsuranceBaseInfoMapper getInsuranceBaseInfoMapper() {
        return MapperProxyFactory.getProxy(InsuranceBaseInfoMapper.class);
    }

    public AuthService getAuthService(User user) {
        return ServiceUtil.getService(AuthServiceImpl.class, user);
    }

    @Override
    public void save(RecessionParam param, Long employeeId) {

        //入参判断
        if (StringUtils.isBlank(param.getPaymentOrganization()) || StringUtils.isBlank(param.getBillMonth()) || CollectionUtils.isEmpty(param.getRecessionMonthList())
                || CollectionUtils.isEmpty(param.getIncludes()) || CollectionUtils.isEmpty(param.getProjects())) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "参数错误"));
        }
        //需要退差的员工
        List<Long> employeeIds = param.getIncludes();
        if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(param.getExcludes())) {
            employeeIds = employeeIds.stream().filter(item -> !param.getExcludes().contains(item)).collect(Collectors.toList());
        }
        SalaryAssert.notEmpty(employeeIds, SalaryI18nUtil.getI18nLabel(133967, "无退差人员"));

        //查询退差员工对应的退差月份的正常缴纳的福利台账数据
        List<InsuranceAccountDetailPO> detailPOS = new ArrayList<>();
        List<List<Long>> partitionEmpIds = Lists.partition((List<Long>) employeeIds, 100);
        partitionEmpIds.forEach(p -> {
            detailPOS.addAll(
                    getInsuranceAccountDetailMapper().list(InsuranceAccountDetailParam.builder()
                            .paymentStatus(PaymentStatusEnum.COMMON.getValue())
                            .recessionMonthList(param.getRecessionMonthList())
                            .employeeIds(p)
                            .paymentOrganization(param.getPaymentOrganization())
                            .build())
            );
        });

        encryptUtil.decryptList(detailPOS, InsuranceAccountDetailPO.class);
        //处理数据
        List<InsuranceAccountDetailPO> finalDetailPOS = detailPOS;
        List<InsuranceAccountDetailPO> finalRecessionDetails = new ArrayList<>();

        // 获取员工信息
        List<DataCollectionEmployee> employeeList = getSalaryEmployeeService(user).listByIds(employeeIds);
        Map<Long, DataCollectionEmployee> employeeMap = SalaryEntityUtil.convert2Map(employeeList, DataCollectionEmployee::getEmployeeId);
        employeeIds.forEach(id -> {
            DataCollectionEmployee employee = employeeMap.getOrDefault(id, DataCollectionEmployee.builder().build());
            param.getRecessionMonthList().forEach(billMonth -> {
                Optional<InsuranceAccountDetailPO> detailPOOptional = finalDetailPOS.stream().filter(
                        detail -> Objects.equals(billMonth, detail.getBillMonth())
                                && Objects.equals(id, detail.getEmployeeId())).findFirst();
                detailPOOptional.ifPresent(insuranceAccountDetailPO -> recessionAccount(param, insuranceAccountDetailPO, finalRecessionDetails, employee));
            });
        });
        //退差数据入库
        if (CollectionUtils.isNotEmpty(finalRecessionDetails)) {
            encryptUtil.encryptList(finalRecessionDetails, InsuranceAccountDetailPO.class);

            //删除已有退差记录
            finalRecessionDetails.forEach(getInsuranceAccountDetailMapper()::deleteRecessionData);
            //入库新数据
            List<List<InsuranceAccountDetailPO>> partition = Lists.partition((List<InsuranceAccountDetailPO>) finalRecessionDetails, 20);
            partition.forEach(getInsuranceAccountDetailMapper()::batchSaveAccountDetails);
            //刷新bill_batch表中统计信息
            getSIAccountService(user).refreshBillBatch(Long.valueOf(param.getPaymentOrganization()), param.getBillMonth());
            //记录操作日志
            encryptUtil.decryptList(finalRecessionDetails, InsuranceAccountDetailPO.class);
            InsuranceAccountBatchPO targetPO = getInsuranceAccountBatchMapper().getByBillMonth(param.getBillMonth(), Long.valueOf(param.getPaymentOrganization()));
            TaxAgentPO taxAgentInfo = getTaxAgentMapper().getById(targetPO.getPaymentOrganization());
            LoggerContext<InsuranceAccountDetailPO> loggerContext = new LoggerContext();
            loggerContext.setUser(user);
            loggerContext.setTargetId(String.valueOf(targetPO.getId()));
            loggerContext.setTargetName(taxAgentInfo.getName() + "-" + targetPO.getBillMonth());
            loggerContext.setOperateType(OperateTypeEnum.ADD.getValue());
            loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "福利台账明细表-新增退差"));
            loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "福利台账明细表-新增退差")
                    + ":" + taxAgentInfo.getName() + "-" + targetPO.getBillMonth());
            finalRecessionDetails.forEach(loggerContext::setNewValues);
            SalaryElogConfig.siAccountLoggerTemplate.write(loggerContext);
        }
    }


    @Override
    public void del(Collection<Long> ids, Long employeeId) {

        //入参判断
        if (CollectionUtils.isEmpty(ids)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "参数错误"));
        }

        InsuranceAccountDetailPO refreshTargetPO = getInsuranceAccountDetailMapper().getById(((List<Long>) ids).get(0));


        //根据id删除
        List<List<Long>> partition = Lists.partition((List<Long>) ids, 100);
        partition.forEach(getInsuranceAccountDetailMapper()::batchDelAccountDetailsByIds);
        //记录操作日志
        LoggerContext<InsuranceAccountDetailPO> loggerContext = new LoggerContext();
        loggerContext.setUser(user);
        loggerContext.setTargetId(ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
        loggerContext.setTargetName(ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
        loggerContext.setOperateType(OperateTypeEnum.DELETE.getValue());
        loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "福利台账明细表-删除退差核算记录"));
        loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "福利台账明细表-删除退差核算记录"));
        SalaryElogConfig.siAccountLoggerTemplate.write(loggerContext);
        //刷新bill_batch表中统计信息
        getSIAccountService(user).refreshBillBatch(refreshTargetPO.getPaymentOrganization(), refreshTargetPO.getBillMonth());
    }


    private void recessionAccount(RecessionParam param, InsuranceAccountDetailPO insuranceAccountDetailPO, List<InsuranceAccountDetailPO> recessionDetails, DataCollectionEmployee employee) {
        List<Integer> projects = param.getProjects();
        InsuranceAccountDetailPO temp = new InsuranceAccountDetailPO();
        recessionBaseBuild(param, temp, insuranceAccountDetailPO, employee);
        if (projects.contains(ProjectTypeEnum.ALL.getValue())) {
            recessionSocial(param, temp, insuranceAccountDetailPO);
            recessionFund(param, temp, insuranceAccountDetailPO);
            recessionOther(param, temp, insuranceAccountDetailPO);
        }
        if (projects.contains(ProjectTypeEnum.SOCIAL.getValue())) {
            recessionSocial(param, temp, insuranceAccountDetailPO);
        }

        if (projects.contains(ProjectTypeEnum.FUND.getValue())) {
            recessionFund(param, temp, insuranceAccountDetailPO);
        }
        if (projects.contains(ProjectTypeEnum.OTHER.getValue())) {
            recessionOther(param, temp, insuranceAccountDetailPO);
        }
        account(temp);
        recessionDetails.add(temp);
    }


    private void recessionBaseBuild(RecessionParam param, InsuranceAccountDetailPO temp, InsuranceAccountDetailPO insuranceAccountDetailPO, DataCollectionEmployee employee) {
        InsuranceAccountBatchPO insuranceAccountBatchPO = getInsuranceAccountBatchMapper().getByBillMonth(param.getBillMonth(), Long.valueOf(param.getPaymentOrganization()));
        temp.setPaymentStatus(PaymentStatusEnum.RECESSION.getValue());
//        temp.setId(IdGenerator.generate());
        temp.setCreateTime(new Date());
        temp.setUpdateTime(new Date());
        temp.setCreator(insuranceAccountBatchPO.getCreator());
        temp.setSocialPayOrg(insuranceAccountDetailPO.getPaymentOrganization());
        temp.setDeleteType(DeleteTypeEnum.NOT_DELETED.getValue());
        temp.setTenantKey(insuranceAccountDetailPO.getTenantKey());
        temp.setEmployeeId(insuranceAccountDetailPO.getEmployeeId());
        temp.setSubcompanyName(employee.getSubcompanyName());
        temp.setSubcompanyId(employee.getSubcompanyid());
        temp.setDepartmentName(employee.getDepartmentName());
        temp.setDepartmentId(employee.getDepartmentId());
        temp.setJobtitleName(employee.getJobtitleName());
        temp.setJobtitleId(employee.getJobtitleId());
        temp.setJobcall(employee.getJobcall());
        temp.setJobcallId(employee.getJobcallId());
        temp.setStatus(employee.getStatus());
        temp.setBillMonth(param.getBillMonth());
        temp.setBillStatus(BillStatusEnum.NOT_ARCHIVED.getValue());
        temp.setBillMonth(param.getBillMonth());
        temp.setBillStatus(BillStatusEnum.NOT_ARCHIVED.getValue());
        temp.setSupplementaryMonth(insuranceAccountDetailPO.getBillMonth());
        temp.setSupplementaryProjects(String.join(",",
                param.getProjects() == null ? new ArrayList<>() : param.getProjects().stream().map(String::valueOf).collect(Collectors.toList())));
        temp.setResourceFrom(insuranceAccountDetailPO.getResourceFrom());
        temp.setSocialAccount(insuranceAccountDetailPO.getSocialAccount());
        temp.setSocialSchemeId(insuranceAccountDetailPO.getSocialSchemeId());
        temp.setSocialPaymentBaseString(insuranceAccountDetailPO.getSocialPaymentBaseString());
        temp.setFundAccount(insuranceAccountDetailPO.getFundAccount());
        temp.setSupplementFundAccount(insuranceAccountDetailPO.getSupplementFundAccount());
        temp.setFundSchemeId(insuranceAccountDetailPO.getFundSchemeId());
        temp.setFundPaymentBaseString(insuranceAccountDetailPO.getFundPaymentBaseString());
        temp.setOtherSchemeId(insuranceAccountDetailPO.getOtherSchemeId());
        temp.setOtherPaymentBaseString(insuranceAccountDetailPO.getOtherPaymentBaseString());
//        temp.setExternalFlag(insuranceAccountDetailPO.getExternalFlag());
        temp.setPaymentOrganization(insuranceAccountDetailPO.getPaymentOrganization());
//        temp.setPaymentAgency(insuranceAccountDetailPO.getPaymentAgency());
        temp.setSocialPaymentComBaseString(insuranceAccountDetailPO.getSocialPaymentComBaseString());
        temp.setFundPaymentComBaseString(insuranceAccountDetailPO.getFundPaymentComBaseString());
        temp.setOtherPaymentComBaseString(insuranceAccountDetailPO.getOtherPaymentComBaseString());
    }

    private void recessionSocial(RecessionParam param, InsuranceAccountDetailPO temp, InsuranceAccountDetailPO insuranceAccountDetailPO) {
        //退差社保个人缴费
        String socialPerJson = insuranceAccountDetailPO.getSocialPerJson();
        if (StringUtils.isNotBlank(socialPerJson)) {
            HashMap<String, String> jsonMap = new HashMap<>();
            HashMap<String, String> hashMap = JSON.parseObject(socialPerJson, new HashMap<String, String>().getClass());
            hashMap.forEach((k, v) -> {
                if (StringUtils.isNotBlank(v)) {
                    v = new BigDecimal(v).negate().toPlainString();
                    jsonMap.put(k, v);
                }
            });
            temp.setSocialPerJson(JSON.toJSONString(jsonMap));
        }
        if (StringUtils.isNotBlank(insuranceAccountDetailPO.getSocialPerSum())) {
            temp.setSocialPerSum(new BigDecimal(insuranceAccountDetailPO.getSocialPerSum()).negate().toPlainString());
        }
        //退差社保单位缴费
        String socialComJson = insuranceAccountDetailPO.getSocialComJson();
        if (StringUtils.isNotBlank(socialComJson)) {
            HashMap<String, String> jsonMap = new HashMap<>();
            HashMap<String, String> hashMap = JSON.parseObject(socialComJson, new HashMap<String, String>().getClass());
            hashMap.forEach((k, v) -> {
                if (StringUtils.isNotBlank(v)) {
                    v = new BigDecimal(v).negate().toPlainString();
                    jsonMap.put(k, v);
                }
            });
            temp.setSocialComJson(JSON.toJSONString(jsonMap));
        }
        if (StringUtils.isNotBlank(insuranceAccountDetailPO.getSocialComSum())) {
            temp.setSocialComSum(new BigDecimal(insuranceAccountDetailPO.getSocialComSum()).negate().toPlainString());
        }
    }

    private void recessionFund(RecessionParam param, InsuranceAccountDetailPO temp, InsuranceAccountDetailPO insuranceAccountDetailPO) {
        //退差公积金个人缴费
        String fundPerJson = insuranceAccountDetailPO.getFundPerJson();
        if (StringUtils.isNotBlank(fundPerJson)) {
            HashMap<String, String> jsonMap = new HashMap<>();
            HashMap<String, String> hashMap = JSON.parseObject(fundPerJson, new HashMap<String, String>().getClass());
            hashMap.forEach((k, v) -> {
                if (StringUtils.isNotBlank(v)) {
                    v = new BigDecimal(v).negate().toPlainString();
                    jsonMap.put(k, v);
                }
            });
            temp.setFundPerJson(JSON.toJSONString(jsonMap));
        }
        if (StringUtils.isNotBlank(insuranceAccountDetailPO.getFundPerSum())) {
            temp.setFundPerSum(new BigDecimal(insuranceAccountDetailPO.getFundPerSum()).negate().toPlainString());
        }
        //退差公积金单位缴费
        String fundComJson = insuranceAccountDetailPO.getFundComJson();
        if (StringUtils.isNotBlank(fundComJson)) {
            HashMap<String, String> jsonMap = new HashMap<>();
            HashMap<String, String> hashMap = JSON.parseObject(fundComJson, new HashMap<String, String>().getClass());
            hashMap.forEach((k, v) -> {
                if (StringUtils.isNotBlank(v)) {
                    v = new BigDecimal(v).negate().toPlainString();
                    jsonMap.put(k, v);
                }
            });
            temp.setFundComJson(JSON.toJSONString(jsonMap));
        }
        if (StringUtils.isNotBlank(insuranceAccountDetailPO.getFundComSum())) {
            temp.setFundComSum(new BigDecimal(insuranceAccountDetailPO.getFundComSum()).negate().toPlainString());
        }
    }


    private void recessionOther(RecessionParam param, InsuranceAccountDetailPO temp, InsuranceAccountDetailPO insuranceAccountDetailPO) {
        //退差其他福利个人缴费
        String otherPerJson = insuranceAccountDetailPO.getOtherPerJson();
        if (StringUtils.isNotBlank(otherPerJson)) {
            HashMap<String, String> jsonMap = new HashMap<>();
            HashMap<String, String> hashMap = JSON.parseObject(otherPerJson, new HashMap<String, String>().getClass());
            hashMap.forEach((k, v) -> {
                if (StringUtils.isNotBlank(v)) {
                    v = new BigDecimal(v).negate().toPlainString();
                    jsonMap.put(k, v);
                }
            });
            temp.setOtherPerJson(JSON.toJSONString(jsonMap));
        }
        if (StringUtils.isNotBlank(insuranceAccountDetailPO.getOtherPerSum())) {
            temp.setOtherPerSum(new BigDecimal(insuranceAccountDetailPO.getOtherPerSum()).negate().toPlainString());
        }
        //退差其他福利单位缴费
        String otherComJson = insuranceAccountDetailPO.getOtherComJson();
        if (StringUtils.isNotBlank(otherComJson)) {
            HashMap<String, String> jsonMap = new HashMap<>();
            HashMap<String, String> hashMap = JSON.parseObject(otherComJson, new HashMap<String, String>().getClass());
            hashMap.forEach((k, v) -> {
                if (StringUtils.isNotBlank(v)) {
                    v = new BigDecimal(v).negate().toPlainString();
                    jsonMap.put(k, v);
                }
            });
            temp.setOtherComJson(JSON.toJSONString(jsonMap));
        }
        if (StringUtils.isNotBlank(insuranceAccountDetailPO.getOtherComSum())) {
            temp.setOtherComSum(new BigDecimal(insuranceAccountDetailPO.getOtherComSum()).negate().toPlainString());
        }
    }


    private void account(InsuranceAccountDetailPO insuranceAccountDetailPO) {

        //个人合计
        BigDecimal socialPerson =
                StringUtils.isBlank(insuranceAccountDetailPO.getSocialPerSum()) ? new BigDecimal("0") : new BigDecimal(insuranceAccountDetailPO.getSocialPerSum());
        BigDecimal fundPerson =
                StringUtils.isBlank(insuranceAccountDetailPO.getFundPerSum()) ? new BigDecimal("0") : new BigDecimal(insuranceAccountDetailPO.getFundPerSum());
        BigDecimal otherPerson =
                StringUtils.isBlank(insuranceAccountDetailPO.getOtherPerSum()) ? new BigDecimal("0") : new BigDecimal(insuranceAccountDetailPO.getOtherPerSum());
        BigDecimal perSum = socialPerson.add(fundPerson).add(otherPerson);
        insuranceAccountDetailPO.setPerSum(perSum.toPlainString());
        //单位合计
        BigDecimal socialCom =
                StringUtils.isBlank(insuranceAccountDetailPO.getSocialComSum()) ? new BigDecimal("0") : new BigDecimal(insuranceAccountDetailPO.getSocialComSum());
        BigDecimal fundCom = StringUtils.isBlank(insuranceAccountDetailPO.getFundComSum()) ? new BigDecimal("0") : new BigDecimal(insuranceAccountDetailPO.getFundComSum());
        BigDecimal otherCom =
                StringUtils.isBlank(insuranceAccountDetailPO.getOtherComSum()) ? new BigDecimal("0") : new BigDecimal(insuranceAccountDetailPO.getOtherComSum());
        BigDecimal comSum = socialCom.add(fundCom).add(otherCom);
        insuranceAccountDetailPO.setComSum(comSum.toPlainString());
        //社保合计
        insuranceAccountDetailPO.setSocialSum(socialPerson.add(socialCom).toPlainString());
        //公积金合计
        insuranceAccountDetailPO.setFundSum(fundPerson.add(fundCom).toPlainString());
        //其他福利合计
        insuranceAccountDetailPO.setOtherSum(otherPerson.add(otherCom).toPlainString());
        //合计
        insuranceAccountDetailPO.setTotal(perSum.add(comSum).toPlainString());
    }


    /**
     * 获取当前登录人所控制的个税扣缴义务人的人员范围
     */
    @Override
    public PageInfo<HrmInfoDTO> getEmployeeListByTaxAgent(HrmQueryParam param) {

        List<InsuranceArchivesBaseInfoPO> list = getInsuranceBaseInfoMapper().listAll();
        list = getAuthService(user).auth(list, AuthFilterTypeEnum.ADMIN_DATA, InsuranceArchivesBaseInfoPO.class);
        List<Long> employeeIds = SalaryEntityUtil.properties(list, InsuranceArchivesBaseInfoPO::getEmployeeId, Collectors.toList());
        List<HrmInfoDTO> resultData = new ArrayList<>();
        if (employeeIds.size() > 0) {
            List<List<Long>> partition = Lists.partition(employeeIds, 1000);
            partition.forEach(p -> {
                param.setIds(p);
                resultData.addAll(getEmployMapper().listHrmInfoByIdAndName(param));
            });

        }

        // 分页
        PageInfo<HrmInfoDTO> page = new PageInfo<>();
        if (resultData.size() == 0) {
            return page;
        }
        page.setTotal(resultData.size());

        page.setList(SalaryPageUtil.subList(param.getPageNum(), param.getPageSize(), resultData));
        page.setPageSize(param.getPageSize());
        page.setPageNum(param.getPageNum());


        return page;
    }
}
